#!/usr/bin/env python
# -*- coding:utf-8 -*-


'''
@Author : shouke
'''

import logging
import json
import urllib.request

from utils.utils import Utils
from components.decoraters.action_decorater import ActionDecorator

logger = logging.getLogger(__name__)


class MyHttpRequest(object):
    @classmethod
    def urlencode(self, data, safe='=&'):
        '''
        对请求参数进行url编码
        :param method http请求方法
        :param data 待编码数据
        :param safe 安全字符
        '''

        try:
            if isinstance(data, dict): # 字典数据
                data = urllib.parse.urlencode(data)  # 将参数转为url编码字符串# 注意，此处 data为必须为字典类型的字符串数据
            elif data:
                data = str(data)
                if safe:
                    urllib.parse.quote(data, safe=safe)
                elif data:
                    urllib.parse.quote(data)
        except Exception as e:
            logger.error('%s' % e)
        return data


    @ActionDecorator.action_register('send_request', 'MyHttpRequest', 'send_request', __name__)
    @classmethod
    def send_request(self, config:dict, *args, **kwargs):
        '''
        发送请求
        :param config 配置信息，具体如下
        {
            "method": "POST",
            "path": "/api/v1/login", #可以是绝对URL， 形如http: //127.0.0.1:8002/api/v1/login，也可以是相对URL,形如 /api/v1/login
            "body": { #如果json配置了数据，则这里保持为None
                "account": "${username}",
                 "password": "${password}"
            },
            "params":None, # 可选配置，默认为None
            "stream": False,
            "headers": {
                "Content-Type": "application/json"
            },
            "auth": None,
            "allow_redirects": True,
             #children可选
            "children": [{ # 如果不配置响应断言，系统会执行默认断言 响应状态码为 200，201，301则算通过
                            "action": "assert_response",
                            "name": "请求响应断言",
                            "target": "body", # 断言目标，暂时支持body - 响应体,header - 响应头,status - 响应状态码
                            "rule": "assert_contain", #模式要匹配的规则,目前支持 assert_contain 包含  assert_equal 等于
                            "patterns": ["赖玉", "token"], # 模式 [模式1，模式2，模式3]
                            "logic":"or" # 模式之间的逻辑关系 and 、or  可不配置logic配置项，默认为and
                        }, {
                            "action": "extract_by_regexp",#正则表达式提取器
                            "name":"正则表达式提取器",
                            "refName": "userInfo", #引用名称 # 不能为空
                            "express": 'username":"(.+?)",.+token":"(.+?)",', #正则表达式
                            "target": "body", # 提取源对象
                            "template": "$1$$2$$0$", # 默认为$0$ $0$-取分组0数据（分组1到分组n按顺序拼接起来的数据） $1$-取分组1数据 $2$-取分组2数据 以此类推,如果给定索引越界
                            "matchNo": 0 # 默认为0，取分组中指定匹配索引的数据 matchNo 0开始，0表示随机，1表示取分组中第一个值 2 取第二个分组的值 以此类推， 如果越界，提取结果为空
                        },{..其它action}]
            }
        '''
        user = kwargs['user']
        user_resource_dict = user.user_resource_dict[user.chain_id]

        config = Utils.analyze_data(config, user_resource_dict)
        logger.debug('正准备发送请求')
        name = config.get('name')
        logger.debug('【请求名称】：%s' % name)
        method = config.get('method')
        logger.debug('【请求方法】：%s' % method)
        method = method.lower()
        path = config.get('path')
        logger.debug('【请求路径】：%s' % path)
        headers = config.get('headers')
        logger.debug('【请求头】：%s' % headers)
        params = config.get('params')
        logger.debug('【请求参数(params)】：%s' % str(params))
        body = config.get('body')
        logger.debug('【请求参数(body)】：%s' % str(body))
        auth = config.get('auth')
        logger.debug('【请求鉴权】：%s' % config.get('auth'))
        logger.debug('【是否发送流数据】：%s' % config.get('stream'))
        logger.debug('【是否允许重定向】：%s' % config.get('allow_redirects'))
        if not headers:
            headers = {}

        if "Content-Type" not in headers and "content-type" not in headers: # 如果未配置Content-Type
            # 如果发起的请求为get请求，且params不为空、None等False值，则默认设置 Content-Type 为 application/x-www-form-urlencoded;charset=utf-8
            # 如果发起的请求为post请求，且params不为空、None等False值, 且body为空、None等False值，则默认设置 Content-Type 为 application/x-www-form-urlencoded;charset=utf-8
            if (method == 'get' and params) or (method == 'post' and params and not body):
                headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'
            elif isinstance(body, dict): # 如果body为字典，则默认设置 Content-Type 为 application/json;charset=utf-8
                headers['Content-Type'] = 'application/json;charset=utf-8'

        content_type_header = str(headers.get('Content-Type') or headers.get('content-type'))
        if 'application/x-www-form-urlencoded' in content_type_header:
            params = self.urlencode(params)
            if isinstance(params, str) and params:
                path = path + '?' + params
            body = self.urlencode(body)
            if body and isinstance(body, str):
                body = body.encode('utf-8')

        elif 'application/json' in content_type_header:
            if isinstance(body, dict):
                body = json.dumps(body).encode('utf-8')

        stream = config.get('stream') or False
        allow_redirects = config.get('allow_redirects') or True


        temp_config = {
            "请求名称":name,
            "请求方法":method,
            "请求主机":user.environment.host,
            "请求路径":path,
            "请求头":headers,
            "请求参数(params)":params,
            "请求参数(body)":body,
            "请求鉴权":auth,
            "是否发送流数据":stream,
            "是否允许重定向":allow_redirects
        }
        logger.debug('【实际发送请求配置】：\n%s' %  temp_config)
        response = getattr(user.client, config['method'].lower())(path,
                                                     name=config.get('name'),
                                                     data=body,
                                                     headers=headers,
                                                     auth=auth,
                                                     catch_response=True,
                                                     stream=stream,
                                                     allow_redirects=allow_redirects)
        logger.debug('【请求响应状态码】：%s' % response.status)
        logger.debug('【请求响应头(未格式化)】：%s' % response.headers)
        logger.debug('【请求响应头】：\n%s' % json.dumps(response.headers, indent=2))
        logger.debug('【请求响应体(未格式化)】：%s' % response.text)
        try:
            logger.debug('【请求响应体】：\n%s' % json.dumps(response.json(), indent=2, ensure_ascii=False))
            user_resource_dict['body'] = response.json()
        except Exception as e:
            logger.debug('格式化响应体为json数据格式出错，取消格式化操作')
            user_resource_dict['body'] = response.text

        children = config.get('children')
        if not children:
            logger.debug('该请求未配置断言，正在执行默认断言')
            user.assert_response_default(response)
            return

        assert_response_set = 0 # 标记是否存在响应断言
        for item in children:
            item['response'] = response
            if not assert_response_set and item['action'] == 'assert_response':
                assert_response_set = 1

        if not assert_response_set: # 如果存在响应断言，则执行默认的断言 # 因为捕获了请求，如果不进行断言，统计数据那边会缺失
             user.assert_response_default(response)
        user.run_actions(children)


